/**
 * 现有测试文件上下文提取器
 * 
 * 问题：当追加UT到现有文件时，LLM不知道：
 * - 测试类定义（class XXXTest）
 * - 成员变量（instance_, mockXxx_）
 * - SetUp/TearDown的初始化逻辑
 * - 其他测试函数的写法模式
 * 
 * 解决：读取现有测试文件，提取上下文信息
 */

import * as fs from 'fs';
import * as path from 'path';

export interface ExistingTestContext {
    // 测试文件基本信息
    testFilePath: string;
    exists: boolean;
    
    // 测试类信息
    testClassName?: string;
    testClassNamespace?: string;
    
    // 成员变量
    memberVariables: {
        name: string;           // 变量名，如 instance_
        type: string;           // 类型，如 std::shared_ptr<FileAccessService>
        isMock: boolean;        // 是否是Mock对象
        isInstance: boolean;    // 是否是被测对象实例
    }[];
    
    // SetUp/TearDown
    hasSetUp: boolean;
    hasTearDown: boolean;
    setupCode?: string;
    teardownCode?: string;
    
    // 辅助函数
    helperFunctions: {
        name: string;
        signature: string;
        isStatic: boolean;
    }[];
    
    // 现有测试函数（用于学习写法）
    existingTests: {
        name: string;
        fullCode: string;       // 完整代码
        usesInstance: boolean;  // 是否使用instance_
        usesMocks: string[];    // 使用的Mock对象列表
        pattern: string;        // 测试模式（simple/mock/complex）
    }[];
    
    // 头文件包含
    includes: string[];
    
    // Mock类定义
    mockClasses: {
        name: string;
        baseClass: string;
        methods: string[];
    }[];
}

export class ExistingTestContextExtractor {
    
    /**
     * 提取现有测试文件的上下文
     */
    public static async extractContext(testFilePath: string): Promise<ExistingTestContext> {
        const context: ExistingTestContext = {
            testFilePath,
            exists: false,
            memberVariables: [],
            hasSetUp: false,
            hasTearDown: false,
            helperFunctions: [],
            existingTests: [],
            includes: [],
            mockClasses: []
        };
        
        // 检查文件是否存在
        if (!fs.existsSync(testFilePath)) {
            console.log(`   ℹ️ 测试文件不存在，将创建新文件: ${testFilePath}`);
            return context;
        }
        
        context.exists = true;
        console.log(`   ✓ 检测到现有测试文件: ${path.basename(testFilePath)}`);
        
        // 读取文件内容
        const content = fs.readFileSync(testFilePath, 'utf-8');
        
        // 提取各种信息
        this.extractTestClassName(content, context);
        this.extractMemberVariables(content, context);
        this.extractSetUpTearDown(content, context);
        this.extractHelperFunctions(content, context);
        this.extractExistingTests(content, context);
        this.extractIncludes(content, context);
        this.extractMockClasses(content, context);
        
        // 输出统计信息
        console.log(`      - 测试类: ${context.testClassName || 'Unknown'}`);
        console.log(`      - 成员变量: ${context.memberVariables.length}个`);
        console.log(`      - 现有测试: ${context.existingTests.length}个`);
        console.log(`      - Mock类: ${context.mockClasses.length}个`);
        
        return context;
    }
    
    /**
     * 提取测试类名
     */
    private static extractTestClassName(content: string, context: ExistingTestContext): void {
        // 匹配测试类定义
        const classPattern = /class\s+(\w+)\s*:\s*public\s+testing::Test/;
        const match = content.match(classPattern);
        
        if (match) {
            context.testClassName = match[1];
            
            // 提取namespace
            const nsPattern = new RegExp(`namespace\\s+(\\w+)\\s*\\{[\\s\\S]*?class\\s+${context.testClassName}`);
            const nsMatch = content.match(nsPattern);
            if (nsMatch) {
                context.testClassNamespace = nsMatch[1];
            }
        }
    }
    
    /**
     * 提取成员变量
     */
    private static extractMemberVariables(content: string, context: ExistingTestContext): void {
        if (!context.testClassName) return;
        
        // 找到测试类定义
        const classStart = content.indexOf(`class ${context.testClassName}`);
        if (classStart === -1) return;
        
        // 找到类的结束
        let braceCount = 0;
        let inClass = false;
        let classEnd = classStart;
        
        for (let i = classStart; i < content.length; i++) {
            if (content[i] === '{') {
                braceCount++;
                inClass = true;
            } else if (content[i] === '}') {
                braceCount--;
                if (inClass && braceCount === 0) {
                    classEnd = i;
                    break;
                }
            }
        }
        
        const classBody = content.substring(classStart, classEnd);
        
        // 提取protected和private成员变量
        const memberPattern = /(std::shared_ptr<(\w+)>|std::unique_ptr<(\w+)>|sptr<(\w+)>|(\w+)\s*\*)\s+(\w+_)\s*(?:=\s*nullptr)?;/g;
        let match;
        
        while ((match = memberPattern.exec(classBody)) !== null) {
            const varName = match[6];
            const fullType = match[1];
            const innerType = match[2] || match[3] || match[4] || match[5];
            
            context.memberVariables.push({
                name: varName,
                type: fullType,
                isMock: /Mock/i.test(innerType),
                isInstance: /instance_/i.test(varName)
            });
        }
    }
    
    /**
     * 提取SetUp和TearDown
     */
    private static extractSetUpTearDown(content: string, context: ExistingTestContext): void {
        // 提取SetUp
        const setupPattern = /void\s+\w+::SetUp\s*\([^)]*\)\s*(?:override)?\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/;
        const setupMatch = content.match(setupPattern);
        
        if (setupMatch) {
            context.hasSetUp = true;
            context.setupCode = setupMatch[1].trim();
        }
        
        // 提取TearDown
        const teardownPattern = /void\s+\w+::TearDown\s*\([^)]*\)\s*(?:override)?\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/;
        const teardownMatch = content.match(teardownPattern);
        
        if (teardownMatch) {
            context.hasTearDown = true;
            context.teardownCode = teardownMatch[1].trim();
        }
    }
    
    /**
     * 提取辅助函数
     */
    private static extractHelperFunctions(content: string, context: ExistingTestContext): void {
        // 提取SetNativeToken等辅助函数
        const helperPattern = /(static\s+)?void\s+(\w+)\s*\([^)]*\)/g;
        let match;
        
        while ((match = helperPattern.exec(content)) !== null) {
            const funcName = match[2];
            
            // 排除标准函数
            if (!['SetUp', 'TearDown', 'SetUpTestCase', 'TearDownTestCase'].includes(funcName)) {
                context.helperFunctions.push({
                    name: funcName,
                    signature: match[0],
                    isStatic: !!match[1]
                });
            }
        }
    }
    
    /**
     * 提取现有测试函数
     */
    private static extractExistingTests(content: string, context: ExistingTestContext): void {
        // 匹配HWTEST_F或TEST_F
        const testPattern = /(HWTEST_F|TEST_F)\s*\(\s*(\w+)\s*,\s*(\w+)\s*(?:,\s*[^)]+)?\s*\)\s*\{/g;
        let match;
        
        while ((match = testPattern.exec(content)) !== null) {
            const testName = match[3];
            const startPos = match.index;
            const startBrace = content.indexOf('{', startPos);
            
            // 找到函数结束
            let braceCount = 1;
            let endPos = startBrace + 1;
            
            while (endPos < content.length && braceCount > 0) {
                if (content[endPos] === '{') braceCount++;
                if (content[endPos] === '}') braceCount--;
                endPos++;
            }
            
            const fullCode = content.substring(startPos, endPos);
            const bodyCode = content.substring(startBrace + 1, endPos - 1);
            
            // 分析测试模式
            const usesInstance = /instance_->|instance_\./.test(bodyCode);
            const usesMocks: string[] = [];
            
            // 提取使用的Mock对象
            const mockUsagePattern = /(\w+_)->/g;
            let mockMatch;
            while ((mockMatch = mockUsagePattern.exec(bodyCode)) !== null) {
                const varName = mockMatch[1];
                if (context.memberVariables.some(v => v.name === varName && v.isMock)) {
                    if (!usesMocks.includes(varName)) {
                        usesMocks.push(varName);
                    }
                }
            }
            
            // 判断模式
            let pattern = 'simple';
            if (usesMocks.length > 0) {
                pattern = 'mock';
            }
            if (bodyCode.length > 500 || /for\s*\(|while\s*\(/.test(bodyCode)) {
                pattern = 'complex';
            }
            
            context.existingTests.push({
                name: testName,
                fullCode,
                usesInstance,
                usesMocks,
                pattern
            });
        }
    }
    
    /**
     * 提取头文件包含
     */
    private static extractIncludes(content: string, context: ExistingTestContext): void {
        const includePattern = /#include\s+[<"]([^>"]+)[>"]/g;
        let match;
        
        while ((match = includePattern.exec(content)) !== null) {
            context.includes.push(match[1]);
        }
    }
    
    /**
     * 提取Mock类定义
     */
    private static extractMockClasses(content: string, context: ExistingTestContext): void {
        // 匹配Mock类定义
        const mockClassPattern = /class\s+(Mock\w+)\s*:\s*public\s+(\w+)\s*\{([^}]*(?:\{[^}]*\}[^}]*)*)\}/g;
        let match;
        
        while ((match = mockClassPattern.exec(content)) !== null) {
            const mockName = match[1];
            const baseClass = match[2];
            const classBody = match[3];
            
            // 提取MOCK_METHOD
            const methodPattern = /MOCK_METHOD\s*\([^)]+\)/g;
            const methods = classBody.match(methodPattern) || [];
            
            context.mockClasses.push({
                name: mockName,
                baseClass,
                methods
            });
        }
    }
    
    /**
     * 生成上下文Prompt
     */
    public static generateContextPrompt(context: ExistingTestContext): string {
        if (!context.exists) {
            return `
## 📝 测试文件状态

这是一个**新建的测试文件**，没有现有上下文。

请创建完整的测试文件，包括：
1. 头文件包含
2. 测试类定义
3. 成员变量声明
4. SetUp/TearDown实现
5. 测试用例

`;
        }
        
        let prompt = `
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
## 📝 现有测试文件上下文（非常重要！必须遵守！）
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

### 🚨 CRITICAL: 追加测试到现有文件

当前测试文件已存在，你正在**追加新的测试函数**，必须：

✅ **使用现有的测试类**: \`${context.testClassName}\`
✅ **使用现有的成员变量**: 不要重新定义！
✅ **遵循现有的测试模式**: 参考下面的示例
✅ **不要修改SetUp/TearDown**: 除非必要
✅ **保持一致的命名风格**: 与现有测试保持一致

---

### 1️⃣ 测试类定义

\`\`\`cpp
`;
        
        if (context.testClassNamespace) {
            prompt += `namespace ${context.testClassNamespace} {\n\n`;
        }
        
        prompt += `class ${context.testClassName} : public testing::Test {\n`;
        prompt += `protected:\n`;
        
        // 成员变量
        if (context.memberVariables.length > 0) {
            prompt += `    // ✅ 现有成员变量（直接使用，不要重新定义！）\n`;
            for (const member of context.memberVariables) {
                prompt += `    ${member.type} ${member.name} = nullptr;`;
                if (member.isInstance) {
                    prompt += `  // ✅ 被测对象实例`;
                } else if (member.isMock) {
                    prompt += `  // ✅ Mock对象`;
                }
                prompt += `\n`;
            }
        }
        
        prompt += `};\n`;
        
        if (context.testClassNamespace) {
            prompt += `\n} // namespace ${context.testClassNamespace}\n`;
        }
        
        prompt += `\`\`\`

---

### 2️⃣ SetUp/TearDown（已存在，使用现有逻辑）

`;
        
        if (context.hasSetUp && context.setupCode) {
            prompt += `\`\`\`cpp
void ${context.testClassName}::SetUp() {
${context.setupCode}
}
\`\`\`

**✅ 重要**：
`;
            
            // 分析SetUp中初始化了哪些变量
            for (const member of context.memberVariables) {
                if (context.setupCode.includes(member.name)) {
                    if (member.isInstance) {
                        prompt += `- \`${member.name}\` 已在SetUp中初始化，**直接使用**即可\n`;
                    } else if (member.isMock) {
                        prompt += `- \`${member.name}\` Mock对象已初始化，**可以使用EXPECT_CALL**\n`;
                    }
                }
            }
        } else {
            prompt += `⚠️ 没有SetUp函数，成员变量可能未初始化。\n`;
        }
        
        prompt += `
---

### 3️⃣ 现有测试函数示例（学习写法！）

`;
        
        // 显示前3个测试函数作为示例
        const exampleCount = Math.min(3, context.existingTests.length);
        for (let i = 0; i < exampleCount; i++) {
            const test = context.existingTests[i];
            prompt += `
#### 示例${i + 1}: ${test.name}（${test.pattern}模式）

\`\`\`cpp
${test.fullCode.length > 800 ? test.fullCode.substring(0, 800) + '\n    // ... 省略 ...\n}' : test.fullCode}
\`\`\`

**观察**：
`;
            
            if (test.usesInstance) {
                const instanceVar = context.memberVariables.find(v => v.isInstance);
                if (instanceVar) {
                    prompt += `- ✅ 使用 \`${instanceVar.name}->\` 调用被测对象\n`;
                }
            }
            
            if (test.usesMocks.length > 0) {
                prompt += `- ✅ 使用Mock对象: ${test.usesMocks.join(', ')}\n`;
            }
            
            const assertCount = (test.fullCode.match(/EXPECT_|ASSERT_/g) || []).length;
            prompt += `- ✅ 包含${assertCount}个断言\n`;
        }
        
        prompt += `
---

### 4️⃣ Mock类定义（如果有）

`;
        
        if (context.mockClasses.length > 0) {
            for (const mock of context.mockClasses) {
                prompt += `
#### ${mock.name}

\`\`\`cpp
class ${mock.name} : public ${mock.baseClass} {
public:
    // ${mock.methods.length}个Mock方法
${mock.methods.map(m => `    ${m};`).join('\n')}
};
\`\`\`
`;
            }
        } else {
            prompt += `没有Mock类定义。\n`;
        }
        
        prompt += `
---

### 5️⃣ 辅助函数（如果有）

`;
        
        if (context.helperFunctions.length > 0) {
            for (const helper of context.helperFunctions) {
                prompt += `- ${helper.isStatic ? 'static ' : ''}${helper.signature}\n`;
            }
        } else {
            prompt += `没有辅助函数。\n`;
        }
        
        prompt += `
---

### 🎯 生成新测试函数的要求

1. **✅ 使用现有成员变量**
   - ${context.memberVariables.filter(v => v.isInstance).map(v => `使用 \`${v.name}\` 作为被测对象`).join('\n   - ') || '定义新的成员变量'}
   - ${context.memberVariables.filter(v => v.isMock).map(v => `使用 \`${v.name}\` 作为Mock对象`).join('\n   - ') || '如需Mock，先在测试类中定义'}

2. **✅ 遵循现有命名规范**
   - 测试函数命名: ${context.existingTests[0]?.name.split('_')[0] || 'FunctionName'}_XXX_NNN
   - 变量命名: 与现有测试保持一致

3. **✅ 遵循现有测试模式**
   - ${context.existingTests.find(t => t.pattern === 'simple') ? '简单测试：直接调用+断言' : ''}
   - ${context.existingTests.find(t => t.pattern === 'mock') ? 'Mock测试：EXPECT_CALL+调用+断言' : ''}
   - ${context.existingTests.find(t => t.pattern === 'complex') ? '复杂测试：多步骤+多断言' : ''}

4. **❌ 不要做的事**
   - ❌ 不要重新定义已有的成员变量
   - ❌ 不要修改SetUp/TearDown（除非必要）
   - ❌ 不要使用未定义的变量
   - ❌ 不要改变现有的代码风格

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

**重要：严格遵守以上现有上下文，确保生成的测试函数可以直接追加到文件末尾！**
`;
        
        return prompt;
    }
}

